'''
Created on 22/10/2012

@author: Chris
'''

import git, os, types, re
from time import gmtime, strftime
from util import get_file_size


#=======================================================================================  
def construct_git_args(file_path):
#=======================================================================================  
    '''
    description: construct git args from index, index requires a list as its parameter

    parameter: file_path, file path for git operation
    return   : file_list, list of file path
    '''
    file_list = []
    if isinstance(file_path, list):
        for path in file_path:
            path = path.replace('/','\\')
            file_list.append(path)
    else:
        file_path = file_path.replace('/','\\')
        file_list.append(file_path)
        
    return file_list

#=======================================================================================    
def get_untracked_files(repo_dir, repo=None):
#=======================================================================================
    '''
    description: get the untracked files from git repository object, if the repository 
                 object is none then create it

    parameter: repo_dir, repository directory 
    return   : untracked files in repository
    '''
    
    if repo is None:
        repo = git.Repo(repo_dir)
    return repo.untracked_files

#=======================================================================================    
def get_file_status(file_path, file_property):
#=======================================================================================
    '''
    description: get the state of file, check if it is tracked or not 
    
    parameter: file_property, dictionary of file property, file-name:(file-status, file-path) 
    parameter: file_path, the absolute path of file, in case that files have the same name
    return   : ('untracked', status) or ('tracked', 'none')
    '''
    
    file_path = file_path.replace('\\','/')
    
    file_status = 'none'
    if file_property is None:
        return 'tracked', 'none'
    else:
        if file_path in file_property:
            file_status = file_property[file_path]
            return 'untracked', file_status
        else:
            return 'tracked', 'none'
    
    return 'tracked', 'none'


#=======================================================================================    
def query_file_status(repo):
#=======================================================================================
    '''
    description: get the status of file in git, only handle following status
                 M = modified
                 A = added
                 D = deleted
                 R = renamed
                 ?? = untracked

    parameter: file_name, the name of file which is looked for 
    parameter: file_path, untracked file list which is getted from rpository 
    return   : return files with their status M, A, D, R
    '''
    # record file property
    # file-name: (file-status, file-path)
    file_property = {}
    
    # query file status from git
    status_list = repo.git.status('-s').split('\n')
    
    if (status_list is None) or (len(status_list) < 1):
        return None
    
    if len(status_list) == 1 and status_list[0] == '':
        return None
    
    for item in status_list:
        status = item.split()
        file_name = status[1]
        file_status = status[0]
        
        # concatenate directories to absolute path
        file_path = repo.working_dir + '/'
        file_path = file_path.replace('\\','/')
        absolute_path = file_path + file_name

                
        file_property[absolute_path]=file_status    
    
    return file_property
        
#=======================================================================================    
def get_file_cmit_history(file_name, repo):
#======================================================================================= 
    '''
    description: find all blobs in git which is associated with file 

    parameter: file_name, the name of file which is looked for 
    parameter: repo, is the repository object 
    return   : blob and commit objects list [(blob, commit)]
    '''
    
    commits = repo.commit('master')
    blob_list = []
    for parent_commit in commits.iter_parents():
        for blob in parent_commit.tree.blobs:
            if blob.name == file_name:
                blob_list.append((blob, parent_commit))
    return blob_list     

#=======================================================================================    
def get_file_history_records(file_name, repo):
#=======================================================================================
    '''
    description: find all the information about given file save in git repository 

    parameter: file_name, the name of file which is looked for  
    parameter: repo, is the repository object
    return   : dictionary commit:(blob,commit-time, commit-comment)
    '''
    
    commit_history = get_file_cmit_history(file_name, repo)
    if commit_history is None:
        return None
    # key: commit, value: (blob, timestampe, comment)
    file_histroy = {}
    for blob, commit in commit_history:
        
        timestampe = strftime("%a, %d %b %Y %H:%M:%S", gmtime(commit.committed_date))
        file_histroy[commit] = (blob, timestampe,commit.message)
#        file_histroy.append((timestampe,commit.message))
    
    return file_histroy


#=======================================================================================    
def is_valid_repo_dir(repo_dir):
#=======================================================================================
    '''
    description: check if the given directory is a valid git repository 

    parameter: repo_dir, repository directory 
    return   : boolean, return true if the given directory is git
    '''
    
    if not os.path.isdir(repo_dir):
        return False
    
    # git repository always has hidden folder ".git"
    git_db_path = repo_dir + "\\.git"
    if not os.path.isdir(git_db_path):
        return False
    
    return True

#=======================================================================================    
def create_git_repository(repo_dir):
#=======================================================================================
    '''
    description: create git by given repository directory  

    parameter: repo_dir, repository directory 
    return   : (boolean, repo), return true if create git successfully otherwise false
    '''
    if not os.path.isdir(repo_dir):
        return False, None
    
    repo = git.Repo.init(repo_dir)
    if repo is None:
        return False, None
    
    return True, repo


#=======================================================================================    
def add_file_to_git(repo, file_path, comment):
#=======================================================================================  
    '''
    description: add files to control of git 

    parameter: repo, which is the repository object
    parameter: file_list, the files will be added to repository, e.g. ['t1.txt', 'd1.docx']
    parameter: comment, the comment for this commit
    '''
    
    # construct parameter for gitpython
    file_list = construct_git_args(file_path)
    
    index = repo.index
    file_property = query_file_status(repo)
    
    # if this is new file which is not under control of git
    # then add it to index
    status = get_file_status(file_path, file_property)[1]
    if status == '??' or status == 'M':
        try:
            index.add(file_list)
        except WindowsError:
            repo.git.add(file_path)
    index.commit(comment)
    
#=======================================================================================    
def remove_file_from_git(repo, file_path, comment):
#=======================================================================================  
    '''
    description: remove files from control of git but not actually delete the file on 
                 working directory

    parameter: repo, is the repository object
    parameter: file_list, the files will be removed from repository, e.g. ['t1.txt', 'd1.docx']
    parameter: comment, the comment for this commit
    '''
    
    # construct parameter for gitpython
    file_list = construct_git_args(file_path)
    
    index = repo.index
    index.remove(file_list)
    index.commit(comment)


#=======================================================================================    
def rename_file_in_git(repo, file_path, new_file_path, comment):
#=======================================================================================  
    '''
    description: rename a file under the control of git

    parameter: repo, is the repository object
    parameter: file_path, the old path
    parameter: new_file_path, the new file path
    parameter: comment, the comment for this commit
    '''

    index = repo.index
    git = repo.git
    git.mv(file_path, new_file_path)
    index.commit(comment)
    
#=======================================================================================    
def chekout_file_version_from_git(blob, new_file_path):
#=======================================================================================  
    '''
    description: check out a file version from git repository

    parameter: blob, the git blob which is written to new file
    parameter: new_file_path, the target new file 
    '''
    
    # write blob content to file
    
    f = open(new_file_path, 'w+b')
    blob.stream_data(f)
    f.close()


#=======================================================================================
def search_file(file_name, keywords, repo):
#=======================================================================================
    '''
    description: search files which have keywords in their comments 
        
    parameter: file_name, the name of target file looked for
    parameter: keywords, keywords in comment
    parameter: repo, the repository object
    return   : commit and blob objects dictionary
    '''
    
    commits = repo.commit('master')
    # key: commit, value: (blob, timestampe, comment)
    file_histroy = {}
    
    for commit in commits.iter_parents():
        for blob in commit.tree.blobs:
            if blob.name == file_name:
                # get comments from commit
                comment = commit.message
                # if comment has keywords then add it to list
                pattern = re.compile(keywords)
                result = pattern.findall(comment)
                if len(result) != 0:
                    timestampe = strftime("%a, %d %b %Y %H:%M:%S", gmtime(commit.committed_date))
                    file_histroy[commit] = (blob, timestampe,commit.message)
                    
    return file_histroy         
 

#=======================================================================================
def commit_all_files(repo, comment):
#=======================================================================================
    '''
    description: commit all files in the specified repository
        
    parameter: repo, repository object
    parameter: comment, comment for commit
    '''    
    
    index = repo.index
    file_property_dic = query_file_status(repo)
    
    # no files need to be handled
    if file_property_dic == None:
        return
    elif len(file_property_dic) == 0:
        return
    
    for file_status, fil_path in file_property_dic.iteritems():
        # add non-empty and untracked file to index
        if file_status == '??' and get_file_size(fil_path) != 0:
            index.add(file_status)
    
    index.commit(comment)

        